package com.mingserve.common.core.utils;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.zip.*;

/**
 * Zip工具类，支持GZip、ZLib
 *
 * @author lvwshuai
 * @date 2022-12-14
 */
public class ZipUtils {
    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

    // ----------------------------------------------------------------------------- Gzip

    /**
     * Gzip压缩字符串处理 (采用UTF-8编码)
     *
     * @param str 被压缩的字符串
     * @return 压缩后的字节流
     */
    public static byte[] gzip(String str)
    {
        return gzip(str, DEFAULT_CHARSET);
    }

    /**
     * Gzip压缩字符串处理
     *
     * @param str     被压缩的字符串
     * @param charset 编码
     * @return 压缩后的字节流
     */
    public static byte[] gzip(String str, Charset charset)
    {
        if (str == null || str.isEmpty()) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip;
        try {
            gzip = new GZIPOutputStream(out);
            gzip.write(str.getBytes(charset));
            gzip.close();
        } catch (IOException e) {
//            log.error("gzip compress error.", e);
        }
        return out.toByteArray();
    }

    /**
     * Gzip压缩处理
     *
     * @param bytes 被压缩的字节流
     * @return 压缩后的字节流
     */
    public static byte[] gzip(byte[] bytes)
    {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        try {
            GZIPInputStream unGzip = new GZIPInputStream(in);
            byte[] buffer = new byte[256];
            int n;
            while ((n = unGzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
        } catch (IOException e) {
//            log.error("gzip decompress error.", e);
        }

        return out.toByteArray();
    }

    /**
     * Gzip解压缩处理 (采用UTF-8编码)
     *
     * @param bytes   压缩过的字节流
     * @param charset 编码
     * @return 解压后的字符串
     */
    public static String unGzipToStr(byte[] bytes, Charset charset)
    {
        return unGzip(bytes, DEFAULT_CHARSET);
    }

    /**
     * Gzip解压缩处理
     *
     * @param bytes   压缩过的字节流
     * @param charset 编码
     * @return 解压后的字符串
     */
    public static String unGzip(byte[] bytes, Charset charset)
    {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        return new String(unGzip(bytes), charset);
    }

    /**
     * Gzip解压处理
     *
     * @param bytes 压缩过的字节流
     * @return bytes 解压后的字节流
     */
    public static byte[] unGzip(byte[] bytes)
    {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        try {
            GZIPInputStream unGzip = new GZIPInputStream(in);
            byte[] buffer = new byte[256];
            int n;
            while ((n = unGzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
            return out.toByteArray();
        } catch (IOException e) {
//            log.error("gzip uncompress to string error.", e);
        }
        return null;
    }


    // ----------------------------------------------------------------------------- Zlib

    /**
     * Zlib压缩处理 (采用UTF-8编码，默认压缩级别为6)
     *
     * @param str 被压缩的字符串
     * @return 压缩后的字节流
     */
    public static byte[] zlib(String str)
    {
        return zlib(str, DEFAULT_CHARSET);
    }

    /**
     * Zlib压缩处理
     *
     * @param str     被压缩的字符串
     * @param charset 编码
     * @return 压缩后的字节流
     */
    public static byte[] zlib(String str, Charset charset)
    {
        return zlib(str.getBytes(charset));
    }


    /**
     * 打成Zlib压缩包
     *
     * @param bytes 数据
     * @return 压缩后的bytes  压缩级别，0~9
     */
    public static byte[] zlib(byte[] bytes)
    {
        byte[] output = new byte[0];
        Deflater compress = new Deflater();

        compress.reset();
        compress.setInput(bytes);
        compress.finish();
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream(bytes.length)) {
            byte[] buf = new byte[1024];
            while (!compress.finished()) {
                int i = compress.deflate(buf);
                bos.write(buf, 0, i);
            }
            output = bos.toByteArray();
        } catch (Exception e) {
            output = bytes;
//            log.error("compress: ", e);
        } finally {
            compress.end();
        }
        return output;
    }


    /**
     * Zlib解压缩处理 (采用UTF-8编码)
     *
     * @param bytes 压缩过的字节流
     * @return 解压后的字符串
     */
    public static String unZlibToStr(byte[] bytes)
    {
        return unZlib(bytes, StandardCharsets.UTF_8);
    }

    /**
     * Zlib解压缩处理
     *
     * @param bytes   压缩过的字节流 (采用UTF-8编码)
     * @param charset 编码
     * @return 解压后的字符串
     */
    public static String unZlib(byte[] bytes, Charset charset)
    {
        byte[] decompress = unZlib(bytes);
        return new String(decompress, charset);
    }

    /**
     * 解压缩zlib
     *
     * @param bytes 数据
     * @return 解压后的bytes
     */
    public static byte[] unZlib(byte[] bytes)
    {
        byte[] output = new byte[0];

        // Inflater finished() 已到达压缩数据流的末尾，则为true
        Inflater decompress = new Inflater();
        decompress.reset();
        decompress.setInput(bytes);

        long ts = System.currentTimeMillis();
        boolean timeout = false;
        try (ByteArrayOutputStream o = new ByteArrayOutputStream(bytes.length)) {
            byte[] buf = new byte[1024];
            while (!decompress.finished() && !timeout) {
                // 2023-03-13 3秒未执行完，视为超时
                timeout = System.currentTimeMillis() - ts > 3000;
                int i = decompress.inflate(buf);
                o.write(buf, 0, i);
            }
            output = o.toByteArray();
        } catch (Exception e) {
            output = bytes;
//            log.error("decompress: ", e);
        } finally {
            decompress.end();
        }

        return output;
    }

    /**
     * 解压缩
     *
     * @param is 输入流
     * @return byte[] 解压缩后的数据
     */
    public static byte[] unZlib(InputStream is)
    {
        InflaterInputStream iis = new InflaterInputStream(is);
        try (ByteArrayOutputStream o = new ByteArrayOutputStream(1024)) {
            int i = 1024;
            byte[] buf = new byte[i];

            while ((i = iis.read(buf, 0, i)) > 0) {
                o.write(buf, 0, i);
            }
            return o.toByteArray();
        } catch (IOException e) {
//            log.error("decompress: ", e);
        }
        return null;
    }

    /**
     * 读取文件成字节数组
     *
     * @param path
     * @return
     */
    public static byte[] file2byte(String path)
    {
        try {
            FileInputStream in = new FileInputStream(new File(path));
            //当文件没有结束时，每次读取一个字节显示
            byte[] data = new byte[in.available()];
            in.read(data);
            in.close();
            return data;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将字节数组写入文件
     *
     * @param path
     * @param data
     */
    public static void byte2file(String path, byte[] data)
    {
        try {
            FileOutputStream outputStream = new FileOutputStream(new File(path));
            outputStream.write(data);
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取文件成字符串
     *
     * @param path
     * @param encoder
     * @return
     */
    public static String file2str(String path, String encoder)
    {
        StringBuilder sb = new StringBuilder();
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(path), encoder));
            String str = new String();
            while ((str = in.readLine()) != null) {
                sb.append(str);
            }
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    /**
     * 输出字符串到文件
     *
     * @param path
     * @param data
     * @param encoder
     */
    public static void str2file(String path, String data, String encoder)
    {
        try {
            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path), encoder));
            out.write(data);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 按行读取文件成list
     *
     * @param path
     * @param encoder
     * @return
     */
    public static ArrayList<String> file2list(String path, String encoder)
    {
        ArrayList<String> allLine = new ArrayList<>();
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(path), encoder));
            String str = new String();
            while ((str = in.readLine()) != null) {
                allLine.add(str);
            }
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return allLine;
    }

    /**
     * 读取文件成数据矩阵
     *
     * @param path
     * @return
     */
    public static ArrayList<Double> file2matrix(String path) {
        ArrayList<Double> allData = new ArrayList<>();
        try {
            DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(path)));
            //利用DataInputStream来读数据
            while (true) {
                allData.add(in.readDouble());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return allData;
    }

    /**
     * 输出list到文件
     *
     * @param path
     * @param data
     * @param encoder
     */
    public static void list2file(String path, ArrayList<String> data, String encoder)
    {
        try {
            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path), encoder));
            for (String str : data) {
                out.write(str);
                out.newLine();
            }
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 从标准输入中读入
     *
     * @return
     * @throws IOException
     */
    public static String system2str() throws IOException
    {
        BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
        return stdin.readLine();
    }
}